﻿using System;
using System.IO.Ports;
using System.ComponentModel;
using System.Drawing;
using SharpDX.DirectInput;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace ptz
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            listPorts();
            listSpeeds();
            prepareSerial();
            Thread thread = new Thread(new ThreadStart(MainForJoystick));
            thread.IsBackground = true;
            thread.Start();
        }
        private void listPorts()
        {
            comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
            string[] ports = SerialPort.GetPortNames();
            foreach (string port in ports)
            {
                comboBox1.Items.Add(port);
            }
            comboBox1.SelectedIndex = 0;
        }
        private void listSpeeds()
        {
            comboBox2.DropDownStyle = ComboBoxStyle.DropDownList;
            int[] baudrates = new int[6] { 1200, 2400, 4800, 9600, 19200, 38400 };
            foreach (int baudrate in baudrates)
            {
                comboBox2.Items.Add(baudrate);
            }
            comboBox2.SelectedIndex = 1;
        }
        private void MainForJoystick()
        {
            // Initialize DirectInput
            var directInput = new DirectInput();

            // Find a Joystick Guid
            var joystickGuid = Guid.Empty;

            foreach (var deviceInstance in directInput.GetDevices(DeviceType.Gamepad, DeviceEnumerationFlags.AllDevices))
                joystickGuid = deviceInstance.InstanceGuid;

            // If Gamepad not found, look for a Joystick
            if (joystickGuid == Guid.Empty)
                foreach (var deviceInstance in directInput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AllDevices))
                    joystickGuid = deviceInstance.InstanceGuid;

            // If Joystick not found, throws an error
            if (joystickGuid == Guid.Empty)
            {
                BeginInvoke((Action)delegate() { label11.Text = "Joystick: No joystick/Gamepad found."; });
            }
            else
            {
                var joystick = new Joystick(directInput, joystickGuid);

                BeginInvoke((Action)delegate() { label11.Text = "Joystick: Found Joystick/Gamepad with GUID: " + joystickGuid; });

                // Set BufferSize in order to use buffered data.
                joystick.Properties.BufferSize = 128;

                // Acquire the joystick
                joystick.Acquire();

                Point joystickCoords = new Point(128, 128); //start with central coords

                while (true)
                {
                    System.Threading.Thread.Sleep(50);
                    joystick.Poll();
                    var datas = joystick.GetBufferedData();
                    foreach (var state in datas)
                    {
                        //state.Sequence
                        if (state.Offset == JoystickOffset.X)
                        {
                            if (joystickCoords.X != state.Value)
                            {
                                drawLine(joystickCoords.X, joystickCoords.Y, Color.Red);
                                joystickCoords.X = state.Value/255;
                                BeginInvoke((Action)delegate()
                                {
                                    if (pelcoD.JoystickMovable)
                                    {
                                        panControl(joystickCoords.X);
                                        sendSerial();
                                    }
                                });
                            }
                        }
                        else if (state.Offset == JoystickOffset.Y)
                        {
                            if (joystickCoords.Y != state.Value)
                            {
                                drawLine(joystickCoords.X, joystickCoords.Y, Color.Red);
                                joystickCoords.Y = state.Value/255;
                                BeginInvoke((Action)delegate()
                                {
                                    if (pelcoD.JoystickMovable)
                                    {
                                        tiltControl(joystickCoords.Y);
                                        sendSerial();
                                    }
                                });
                            }
                        }
                        else if (state.Offset.ToString() == "Buttons4")
                        {
                            if (state.Value == 0)
                            {
                                BeginInvoke((Action)delegate()
                                {
                                    stopPelco();
                                });
                            }
                            else
                            {
                                BeginInvoke((Action)delegate()
                                {
                                    pelcoD.direction.X = 64;
                                    sendSerial();
                                });
                            }
                        }
                        else if (state.Offset.ToString() == "Buttons6")
                        {
                            if (state.Value == 0)
                            {
                                BeginInvoke((Action)delegate()
                                {
                                    stopPelco();
                                });
                            }
                            else
                            {
                                BeginInvoke((Action)delegate()
                                {
                                    pelcoD.direction.X = 32;
                                    sendSerial();
                                });
                            }
                        }
                        else if (state.Offset.ToString() == "Buttons5")
                        {
                            if (state.Value == 0)
                            {
                                BeginInvoke((Action)delegate()
                                {
                                    pelcoD.JoystickMovable = false;
                                    stopPelco();
                                });
                            }
                            else
                            {
                                BeginInvoke((Action)delegate()
                                {
                                    pelcoD.JoystickMovable = true;
                                });
                            }
                        }
                        else if (state.Offset.ToString() == "Buttons0")
                        {
                            if (state.Value == 0)
                            {
                                BeginInvoke((Action)delegate()
                                {
                                    textBox1.Text = "1";
                                    changeCamera();
                                });
                            }
                        }
                        else if (state.Offset.ToString() == "Buttons1")
                        {
                            if (state.Value == 0)
                            {
                                BeginInvoke((Action)delegate()
                                {
                                    textBox1.Text = "2";
                                    changeCamera();
                                });
                            }
                        }
                        else if (state.Offset.ToString() == "Buttons2")
                        {
                            if (state.Value == 0)
                            {
                                BeginInvoke((Action)delegate()
                                {
                                    textBox1.Text = "3";
                                    changeCamera();
                                });
                            }
                        }
                        else if (state.Offset.ToString() == "Buttons3")
                        {
                            if (state.Value == 0)
                            {
                                BeginInvoke((Action)delegate()
                                {
                                    textBox1.Text = "4";
                                    changeCamera();
                                });
                            }
                        }
                        BeginInvoke((Action)delegate() { label11.Text = state.ToString(); });
                    }
                }
            }
        }
        private void drawLine(int x, int y, Color ncolor)
        {
            pelcoD.line = this.panel1.CreateGraphics();
            pelcoD.line.Clear(panel1.BackColor);
            System.Drawing.Pen showMovement = new System.Drawing.Pen(ncolor);
            pelcoD.line.DrawLine(showMovement, 128, 128, x, y);
            showMovement.Dispose();
            pelcoD.line.Dispose();
        }
        private void panel1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left) // if mouse button held down
            {
                label3.Text = "Operation = Move Camera";
                Size panelcrop = new Size();
                panelcrop.Width = panel1.Size.Width - 2;
                panelcrop.Height = panel1.Size.Height - 2;
                Cursor.Clip = this.RectangleToScreen(new Rectangle(new Point(panel1.Location.X+1, panel1.Location.Y+1), panelcrop)); // confine cursor to panel
                /*label1.Text = "x=" + e.X.ToString();
                label2.Text = "y=" + e.Y.ToString();
                Point mousePos = new Point(e.X, e.Y);
                label1.Text = "Tilt Speed = " + pelcoD.tiltSpeed;
                label2.Text = "Pan Speed = " + pelcoD.panSpeed;*/
                drawLine(e.X, e.Y, Color.Red);
                panControl(e.X);
                tiltControl(e.Y);
                sendSerial();
            }
            else
            {
                Cursor.Clip = Rectangle.Empty; // release cursor from panel
            }
        }
        private void panControl(int x)
        {
            int middle = this.panel1.Size.Width / 2;
            if (x < middle + 2 && x > middle - 2) // if in middle stop panning
            {
                pelcoD.direction.X = 0;
                pelcoD.panSpeed = 0;
            }
            else if (x == 0) // full speed pan to the left
            {
                pelcoD.direction.X = 4;
                pelcoD.panSpeed = 63;
            }
            else if (x > middle - 1)  // variable speed pan to the right
            {
                pelcoD.direction.X = 2;
                pelcoD.panSpeed = (x - middle + 1) / 2;
            }
            else // variable speed pan to the left
            {
                pelcoD.direction.X = 4;
                pelcoD.panSpeed = Math.Abs(middle - x) / 2;
            }
            label2.Text = "Pan Speed = " + pelcoD.panSpeed + " " + x;
        }
        private void tiltControl( int y)
        {
            int middle = this.panel1.Size.Height / 2;
            if (y < middle + 2 && y > middle - 2) // if in middle stop tilting
            {
                pelcoD.direction.Y = 0;
                pelcoD.tiltSpeed = 0;
            }
            else if (y == 0) // full speed tilt up
            {
                pelcoD.direction.Y = 8;
                pelcoD.tiltSpeed = 63;
            }
            else if (y > middle - 1) // variable speed tilt down
            {
                pelcoD.direction.Y = 16;
                pelcoD.tiltSpeed = (y - middle + 1) / 2;
            }
            else // variable speed tilt up
            {
                pelcoD.direction.Y = 8;
                pelcoD.tiltSpeed = Math.Abs(middle - y) / 2;
            }
            label1.Text = "Tilt Speed = " + pelcoD.tiltSpeed + " " + y;
        }
        private void panel1_MouseDown(object sender, MouseEventArgs e) // start sending data as soon as mouse button pressed down
        {
            panel1.Focus();
            panControl(e.X);
            tiltControl(e.Y);
            sendSerial();
        }
        private void sendSerial() // send data through serial port
        {
            int direction = pelcoD.direction.X + pelcoD.direction.Y;
            int checksum = (pelcoD.camaddr + pelcoD.command1 + direction + pelcoD.panSpeed + pelcoD.tiltSpeed) % 256;
            label4.Text = "Hexadecimal = " + "FF" + ":" + pelcoD.camaddr.ToString("X2") + ":" + pelcoD.command1.ToString("X2") + ":" + direction.ToString("X2") + ":" + pelcoD.panSpeed.ToString("X2") + ":" + pelcoD.tiltSpeed.ToString("X2") + ":" + checksum.ToString("X2");
            byte[] c = new byte[]
            {
               0xFF,
               Convert.ToByte(pelcoD.camaddr),
               Convert.ToByte(pelcoD.command1),
               Convert.ToByte(direction),
               Convert.ToByte(pelcoD.panSpeed),
               Convert.ToByte(pelcoD.tiltSpeed),
               Convert.ToByte(checksum)
            };
            try
            {
                pelcoD.selPort.Write(c, 0, c.Length);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Cannot write to serial port!\r\n" + ex);
            }
        }
        private void panel1_MouseUp(object sender, MouseEventArgs e) // send stop command to camera when mouse button released
        {
            stopPelco();
            drawLine(e.X, e.Y, panel1.BackColor);
            label3.Text = "Operation = Stop Camera";
        }
        public class pelcoD // all attributes related to serial and pelco
        {
            public static SerialPort selPort;
            public static int panSpeed = 0;
            public static int tiltSpeed = 0;
            public static int camaddr = 1;
            public static int command1 = 0;
            public static System.Drawing.Graphics line;
            public static Point direction = new Point(0,0); //16=down,8=up,4=left,2=right
            public static bool JoystickMovable = false;
        }
        private void comboBox1_SelectionChangeCommitted(object sender, EventArgs e) // change port binding when a new serial port is selected
        {
            prepareSerial();
        }
        private void prepareSerial()
        {
            try
            {
                if (pelcoD.selPort.IsOpen)
                {
                    pelcoD.selPort.Close();
                    pelcoD.selPort.Dispose();
                }
            }
            catch (Exception ex)
            {
                //do nothing
            }
            pelcoD.selPort = new SerialPort(comboBox1.SelectedItem.ToString(), Convert.ToInt32(comboBox2.SelectedItem), Parity.None, 8, StopBits.One);
            pelcoD.selPort.Handshake = Handshake.None;
            pelcoD.selPort.WriteTimeout = 500;
            /*MessageBox.Show(comboBox1.SelectedItem.ToString());*/
            try
            {
                pelcoD.selPort.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Serial port in use. Select a different one." + ex);
            }
        }
        private void stopPelco()
        {
            pelcoD.panSpeed = 0;
            pelcoD.command1 = 0;
            pelcoD.tiltSpeed = 0;
            pelcoD.direction.X = 0;
            pelcoD.direction.Y = 0;
            sendSerial();
        }
        private void button1_MouseDown(object sender, MouseEventArgs e)
        {
            pelcoD.direction.X = 32;
            sendSerial();
        }

        private void button1_MouseUp(object sender, MouseEventArgs e)
        {
            stopPelco();
        }

        private void button2_MouseDown(object sender, MouseEventArgs e)
        {
            pelcoD.direction.X = 64;
            sendSerial();
        }

        private void button2_MouseUp(object sender, MouseEventArgs e)
        {
            stopPelco();
        }

        private void button3_MouseDown(object sender, MouseEventArgs e)
        {
            pelcoD.direction.X = 128;
            sendSerial();
        }

        private void button3_MouseUp(object sender, MouseEventArgs e)
        {
            stopPelco();
        }

        private void button4_MouseDown(object sender, MouseEventArgs e)
        {
            pelcoD.command1 = 1;
            sendSerial();
        }

        private void button4_MouseUp(object sender, MouseEventArgs e)
        {
            stopPelco();
        }

        private void button5_MouseDown(object sender, MouseEventArgs e)
        {
            pelcoD.command1 = 2;
            sendSerial();
        }

        private void button5_MouseUp(object sender, MouseEventArgs e)
        {
            stopPelco();
        }

        private void button6_MouseDown(object sender, MouseEventArgs e)
        {
            pelcoD.command1 = 4;
            sendSerial();
        }

        private void button6_MouseUp(object sender, MouseEventArgs e)
        {
            stopPelco();
        }

        private void textBox1_Leave(object sender, EventArgs e)
        {
            changeCamera();
        }

        private void changeCamera()
        {
            try
            {
                if (Convert.ToInt16(textBox1.Text) > 255 || Convert.ToInt16(textBox1.Text) < 0)
                {
                    MessageBox.Show("Camera address must be between 0 and 255!");
                    textBox1.Focus();
                    textBox1.SelectionStart = 0;
                    textBox1.SelectionLength = textBox1.Text.Length;
                }
                else
                {
                    pelcoD.camaddr = Convert.ToInt16(this.textBox1.Text);
                    pelcoD.command1 = 8;
                    sendSerial();
                }
            }
            catch (Exception Ex)
            {
                MessageBox.Show("Camera address must be between 0 and 255!");
                textBox1.Focus();
                textBox1.SelectionStart = 0;
                textBox1.SelectionLength = textBox1.Text.Length;
            }
        }

        private void comboBox2_SelectionChangeCommitted(object sender, EventArgs e)
        {
            prepareSerial();
        }
    }
}
